package com.xuzm.system.utils;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 环形map，循环缓冲区或环形缓冲区
 * 满时会移除最早的数据
 */
public class ConcurrentCircularHashMap<K, V> {

    private final ConcurrentMap<K, V> map;
    private final ConcurrentLinkedQueue<K> orderQueue;
    // 一般使用重入锁
    private final Lock lock = new ReentrantLock();
    private final AtomicInteger size;
    private final int maxSize;

    public ConcurrentCircularHashMap(int maxSize) {
        this.maxSize = maxSize;
        this.map = new ConcurrentHashMap<>();
        orderQueue = new ConcurrentLinkedQueue<>();
        this.size = new AtomicInteger(0);
    }

    public V put(K key, V value) {
        V oldValue = map.put(key, value);
        if (oldValue == null) {
            lock.lock();
            try {
                if (size.incrementAndGet() > maxSize) {
                    // 移除最早的数据的key
                    K oldestKey = orderQueue.poll();
                    if (oldestKey != null) {
                        map.remove(oldestKey);
                        size.decrementAndGet();
                    }
                }
                // 将新key添加到队列中
                orderQueue.offer(key);
            }finally {
                lock.unlock();
            }
        }
        return oldValue;
    }

    public V get(K key) {
        return map.get(key);
    }

    public V remove(K key) {
        return map.remove(key);
    }

    public void clear(){
        map.clear();
    }

    public int size() {
        return size.get();
    }

    public static void main(String[] args) {
        ConcurrentCircularHashMap<String, String> circularMap = new ConcurrentCircularHashMap<>(5);

        // Adding elements
        circularMap.put("99", "one");
        circularMap.put("2", "two");
        circularMap.put("8", "three");
        circularMap.put("7", "four");
        circularMap.put("Q", "five");


        // Adding one more element to trigger the circular behavior
        circularMap.put("U", "six");

        // Print the map
        circularMap.map.forEach((k, v) -> System.out.println(k + " -> " + v));
    }
}
